package com.compomics.proteocloud.ui;

import com.compomics.proteocloud.ec2.EC2Instance;
import com.compomics.proteocloud.jobs.CloudJob;
import com.compomics.proteocloud.jobs.DataQueueListener;
import com.compomics.proteocloud.jobs.StatusQueueListener;
import com.compomics.proteocloud.model.DbSearchResult;
import com.compomics.proteocloud.model.DenovoSearchResult;
import com.compomics.proteocloud.s3.StorageObject;
import com.compomics.proteocloud.sqs.MessageManager;
import com.compomics.proteocloud.sqs.MessageObject;
import com.compomics.proteocloud.starter.CloudTools;
import com.compomics.proteocloud.util.*;
import com.compomics.proteocloud.util.SwingWorker;
import com.jgoodies.forms.layout.CellConstraints;
import com.jgoodies.forms.layout.FormLayout;
import com.xerox.amazonws.sqs2.SQSException;
import org.pushingpixels.substance.api.SubstanceLookAndFeel;
import org.pushingpixels.substance.api.skin.BusinessBlackSteelSkin;

import javax.swing.*;
import javax.swing.event.ListSelectionEvent;
import javax.swing.event.ListSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import java.awt.*;
import java.awt.event.*;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.io.File;
import java.net.ConnectException;
import java.net.URL;
import java.util.*;
import java.util.List;

/**
 * This is the com.compomics.proteocloud.CloudGUI class and entry point to the main application.
 * The user has the possibility to select, chunk and upload (on S3 storage) spectra files. Additionally, it's possible to manage the instances + jobs
 * and monitor the Proteocloud communication.
 *
 * @author Thilo Muth
 *
 */
public class ProteoCloudUI extends JFrame {

    /**
     * The CloudGUI proteoCloudUI
     */
    private static ProteoCloudUI proteoCloudUI;

    /**
     * The input files panel
     */
    private JPanel inputPnl;

    /**
     * The storage files panel
     */
    private JPanel storagePnl;

    /**
     * List of spectra files
     */
    private List<File> spectraFiles = new ArrayList<File>();
    

    /**
     * Button for uploading the spectra.
     */
    private JButton uploadSpectraBtn;

    /**
     * The default table model.
     */
    private DefaultTableModel model;

    /**
     * The table for the S3 stored files.
     */
    private JTable storageTbl;

    /**
     * The cloudtools object.
     */
    private CloudTools cloudTools;

    private JPanel jobPnl;

    private JButton startJobBtn;

    private JPanel instancePnl;

    private JLabel instanceNumberLbl;

    private JButton launchInstanceBtn;

    private JTextField instanceNumberTtf;

    private JTextField imageIdTtf;

    private JLabel zoneLbl;

    private JComboBox zoneCbx;

    private Properties props;

    private JButton stopInstanceBtn;

    private DefaultTableModel dataQueueTableMdl;

    private JTable dataQueueTbl;

    private JPanel dataQueuePnl;

    private JLabel spectraFilesLbl;

    private JButton addSpectraBtn;

    private JButton clearSpectraBtn;

    private JTextField spectraFilesTtf;

    private JButton refreshQueueBtn;

    private JPanel jobStatusPnl;

    private JPanel instanceViewPnl;

    private DefaultTableModel instancesTableMdl;

    private JTable instancesTbl;

    private DefaultTableModel jobStatusTableMdl;

    private JTable jobStatusTbl;

    private JButton refreshInstancesBtn;

    private JButton deleteBtn;

    private Map<String, Integer> job2RowMap;

    private Credentials credentials;

    private ProjectSettings settings;

    private MessageManager msgManager;

    private JPanel resultPanel;
    private ResultPanel dbResultPnl;
    private DenovoResultPanel denovoResultPnl;
    private DbSearchResult dbSearchResult;
    private DenovoSearchResult denovoSearchResult;
    private CellConstraints cc =  new CellConstraints();
    private JTabbedPane tabbedPane;
    private StatusPanel statusPnl;
    private JTextField bucketTtf;
    private List<File> chunkedFiles;
    private JTextField messageTtf;
    private JTextField instanceTtf;
    private JButton clearJobsBtn;

    /**
     * Constructor for the ProteoCloudUI frame.
     */
    public ProteoCloudUI() {

        // Application title
        super(PropertiesUI.APPTITLE + " ver. " + PropertiesUI.VERSION);

        // Frame size
        this.setMinimumSize(new Dimension(PropertiesUI.FRAMEWIDTH,	PropertiesUI.FRAMEHEIGHT));
        this.setPreferredSize(new Dimension(PropertiesUI.FRAMEWIDTH,	PropertiesUI.FRAMEHEIGHT));
        proteoCloudUI = this;

        // Get the cloudtools instance
        cloudTools = CloudTools.getInstance();

        // Get the properties object     
        props = AWSUtils.getProperties();

        // Get the content pane
        Container cp = this.getContentPane();

        // Construct the menu
        constructMenu();

        // Construct the input panel
        constructInputPnl();

        // Construct the instance panel
        constructInstancePanel();

        // Construct the storage panel
        contructStoragePanel();

        // Construct the dataqueue panel
        contructDataQueuePanel();

        // Construct the instance com.compomics.proteocloud.view panel
        constructInstanceViewPanel();

        // Construct the job status panel
        constructJobStatusPanel();

        // Result Panel
        resultPanel = new JPanel(new BorderLayout());
        JTabbedPane resultTabbedPane = new JTabbedPane(JTabbedPane.RIGHT);

        dbResultPnl = new ResultPanel(this);
        denovoResultPnl = new DenovoResultPanel(this);
        resultTabbedPane.addTab("Database Search", dbResultPnl);
        resultTabbedPane.addTab("De Novo Search", denovoResultPnl);
        resultPanel.add(resultTabbedPane, BorderLayout.CENTER);

        // Add panels to the content pane
        cp.setLayout(new BorderLayout());

        tabbedPane = new JTabbedPane();

        ImageIcon storageIcon =  new javax.swing.ImageIcon(getClass().getResource("/icons/storage_icon.png"));
        ImageIcon queueIcon =  new javax.swing.ImageIcon(getClass().getResource("/icons/queue_icon.png"));
        ImageIcon instanceIcon =  new javax.swing.ImageIcon(getClass().getResource("/icons/instance_icon.png"));
        ImageIcon statusIcon =  new javax.swing.ImageIcon(getClass().getResource("/icons/status_icon.png"));
        ImageIcon resultsIcon =  new javax.swing.ImageIcon(getClass().getResource("/icons/results_icon.png"));

        tabbedPane.addTab("Storage S3", storageIcon, storagePnl, "The Storage S3");
        tabbedPane.addTab("Data Queue", queueIcon, dataQueuePnl, "The Data Queue");
        tabbedPane.addTab("Instances", instanceIcon, instanceViewPnl, "The Instance View");
        tabbedPane.addTab("Job Status", statusIcon, jobStatusPnl, "The Job Status");
        tabbedPane.addTab("Results View", resultsIcon, resultPanel, "The Results View");
        tabbedPane.setEnabledAt(4, false);
        cp.add(Box.createVerticalStrut(5), BorderLayout.NORTH);
        cp.add(tabbedPane, BorderLayout.CENTER);
        statusPnl = new StatusPanel();
        cp.add(statusPnl, BorderLayout.SOUTH);


        // Set the main icon
        Image image = Toolkit.getDefaultToolkit().getImage(getClass().getResource("/icons/proteocloud_icon.png"));
        setIconImage(image);

        this.addWindowListener(new WindowAdapter() {
            public void windowClosing(WindowEvent e) {
                System.exit(0);
            }
        });
        this.setResizable(true);
        this.pack();

        // Show the startup dialog
        ProjectSettingsDialog projectSettingsDlg = ProjectSettingsDialog.createProjectSettingsDialog(proteoCloudUI, true);

        // Center in the screen
        ScreenConfig.centerInScreen(this);
        this.setVisible(true);
    }

    /**
     * Constructs the instances view panel.
     */
    private void constructInstanceViewPanel() {
        instanceViewPnl = new JPanel(new FormLayout("5dlu, f:p:g, 5dlu, p, 5dlu", "5dlu, t:p, 5dlu"));
        instanceViewPnl.setBorder(BorderFactory.createTitledBorder("Instances Overview"));
        instanceViewPnl.setMaximumSize(new Dimension(instanceViewPnl.getMaximumSize().width, instanceViewPnl.getMaximumSize().height));
        constructInstancesTable();
        if(cloudTools.areCredentialsProvided()) {
            refreshInstancesTable();
        }

        JPanel runningInstancesPnl = new JPanel(new FormLayout("5dlu, f:p:g, 5dlu", "5dlu, t:p, 5dlu"));
        runningInstancesPnl.setBorder(BorderFactory.createTitledBorder("Running Instances"));
        JScrollPane scrollPane = new JScrollPane(instancesTbl);
        runningInstancesPnl.add(scrollPane, cc.xy(2, 2));

        JPanel instanceControlPnl = new JPanel(new FormLayout("5dlu, p, 5dlu", "5dlu, p, 2dlu, p, 7dlu, p, 10dlu, p, 5dlu"));
        instanceControlPnl.setBorder(BorderFactory.createTitledBorder("Instance Control"));

        JLabel instanceLbl = new JLabel("Selected instance:");
        instanceTtf = new JTextField(15);
        instanceTtf.setText("No instance selected");
        instanceTtf.setEditable(false);

        instanceControlPnl.add(instanceLbl, cc.xy(2, 2));
        instanceControlPnl.add(instanceTtf, cc.xy(2, 4));

        // Refresh Instances button
        refreshInstancesBtn = new JButton("Refresh Instances");
        refreshInstancesBtn.setEnabled(false);
        refreshInstancesBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshInstancesTable();
            }
        });

        // Stop button for the instances
        stopInstanceBtn = new JButton("Stop Instance");
        stopInstanceBtn.setEnabled(false);
        stopInstanceBtn.setToolTipText("Terminate the selected instance");
        stopInstanceBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                stopPressed();
            }
        });

        instanceControlPnl.add(stopInstanceBtn, cc.xy(2, 6));
        instanceControlPnl.add(refreshInstancesBtn, cc.xy(2, 8));

        instanceViewPnl.add(runningInstancesPnl, cc.xy(2, 2));
        instanceViewPnl.add(instanceControlPnl, cc.xy(4, 2));
    }

    /**
     * This method constructs the job status panel.
     */
    private void constructJobStatusPanel() {
        jobStatusPnl = new JPanel(new FormLayout("5dlu, f:p:g, 5dlu, p, 5dlu", "5dlu, t:p, 5dlu"));
        jobStatusPnl.setBorder(BorderFactory.createTitledBorder("Job Status"));
        jobStatusPnl.setMaximumSize(new Dimension(jobStatusPnl.getMaximumSize().width, jobStatusPnl.getMaximumSize().height));
        constructJobStatusTable();
        if(cloudTools.areCredentialsProvided()) {
            // Add Property Change Listener to the StatusQueueListener
            StatusQueueListener.getInstance().addPropertyChangeListener(new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    if (evt.getPropertyName().equals("New Jobstatus")){
                        updateTable(evt.getNewValue());
                    }
                }
            });
            refreshStatusTable();
        }

        JPanel runningJobPnl = new JPanel(new FormLayout("5dlu, f:p:g, 5dlu", "5dlu, t:p, 5dlu"));
        runningJobPnl.setBorder(BorderFactory.createTitledBorder("Running Jobs"));
        JScrollPane scrollPane = new JScrollPane(jobStatusTbl);
        runningJobPnl.add(scrollPane, cc.xy(2, 2));

        JPanel jobControlPnl = new JPanel(new FormLayout("5dlu, p, 5dlu", "5dlu, p, 5dlu"));
        jobControlPnl.setBorder(BorderFactory.createTitledBorder("Job Control"));

        // Clears the jobs
        clearJobsBtn = new JButton("Clear Job(s)");
        clearJobsBtn.setEnabled(false);
        clearJobsBtn.setToolTipText("Clears the job(s)");
        clearJobsBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                   clearStatusTable();
            }
        });

        jobControlPnl.add(clearJobsBtn, cc.xy(2, 2));

        jobStatusPnl.add(runningJobPnl, cc.xy(2, 2));
        jobStatusPnl.add(jobControlPnl, cc.xy(4, 2));
    }

    /**
     * Constructs the input panel.
     */
    private void constructInputPnl() {
        inputPnl = new JPanel(new FormLayout("5dlu, p, 5dlu, p, 5dlu, p, 5dlu, p, 5dlu, p, 5dlu", "5dlu, t:p, 5dlu"));
        inputPnl.setBorder(BorderFactory.createTitledBorder("Input Files"));

        // Spectra files label
        spectraFilesLbl = new JLabel("Spectra Files:");

        // Select button for browsing for the MGF files.
        addSpectraBtn = new JButton("Select");
        addSpectraBtn.setToolTipText("Select spectra files");

        // Clear button for removing selected files
        clearSpectraBtn = new JButton("Clear");
        clearSpectraBtn.setToolTipText("Clear spectra files");
        clearSpectraBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                spectraFiles = new ArrayList<File>();
                spectraFilesTtf.setText(spectraFiles.size() + " file(s) selected");
            }
        });

        // Chunked spectra files textfield
        spectraFilesTtf = new JTextField();
        spectraFilesTtf.setEditable(false);
        spectraFilesTtf.setPreferredSize(new Dimension(300, spectraFilesTtf.getPreferredSize().height));
        spectraFilesTtf.setMaximumSize(new Dimension(300, spectraFilesTtf.getPreferredSize().height));
        spectraFilesTtf.setText(spectraFiles.size() + " file(s) selected");

        addSpectraBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // First check whether a file has already been selected.
                // If so, start from that file's parent.
                File startLocation = new File("user.home");
                if (spectraFiles.size() > 0) {
                    File temp = spectraFiles.get(0);
                    startLocation = temp.getParentFile();
                }
                JFileChooser fc = new JFileChooser(startLocation);
                fc.setFileFilter(new ExtensionFileFilter("mgf", false));
                fc.setFileSelectionMode(JFileChooser.FILES_AND_DIRECTORIES);
                fc.setMultiSelectionEnabled(true);
                int result = fc.showOpenDialog(ProteoCloudUI.this);
                if (result == JFileChooser.APPROVE_OPTION) {
                    File[] files = fc.getSelectedFiles();
                    for (int i = 0; i < files.length; i++) {
                        if (files[i].isDirectory()) {
                            File[] currentFiles = files[i].listFiles();
                            for (int k = 0; k < currentFiles.length; k++) {
                                if (fc.getFileFilter().accept(currentFiles[k])) {
                                    spectraFiles.add(currentFiles[k]);
                                }
                            }
                        } else {
                            spectraFiles.add(files[i]);
                        }
                    }
                    spectraFilesTtf.setText(spectraFiles.size() + " file(s) selected");
                    if(spectraFiles.size() > 0){
                        uploadSpectraBtn.setEnabled(true);
                    }
                }
            }
        });

        // Upload button
        uploadSpectraBtn = new JButton("Upload");
        uploadSpectraBtn.setPreferredSize(new Dimension(80, uploadSpectraBtn.getPreferredSize().height));
        uploadSpectraBtn.setToolTipText("Upload chunked spectra files");
        uploadSpectraBtn.setEnabled(false);
        uploadSpectraBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                // Upload
                uploadPressed();

            }
        });

        inputPnl.add(spectraFilesLbl, cc.xy(2, 2));
        inputPnl.add(spectraFilesTtf, cc.xy(4, 2));
        inputPnl.add(addSpectraBtn, cc.xy(6, 2));
        inputPnl.add(clearSpectraBtn, cc.xy(8, 2));
        inputPnl.add(uploadSpectraBtn, cc.xy(10, 2));
    }

    /**
     * Construct the instance panel.
     */
    private void constructInstancePanel() {
        instancePnl = new JPanel();
        instancePnl.setBorder(BorderFactory.createTitledBorder("Instances"));

        // ImageID label
        JLabel imageIdLbl = new JLabel("Image ID (AMI):");

        // Image ID textfield
        imageIdTtf = new JTextField();
        imageIdTtf.setEditable(true);
        imageIdTtf.setPreferredSize(new Dimension(150, imageIdTtf.getPreferredSize().height));
        imageIdTtf.setMaximumSize(new Dimension(150, imageIdTtf.getPreferredSize().height));
        imageIdTtf.setText(props.getProperty(AWSUtils.DEFAULT_WORKER_IMAGE));
        instancePnl = new JPanel();
        instancePnl.setBorder(BorderFactory.createTitledBorder("EC2 Instances"));

        // Zone label
        zoneLbl = new JLabel("Availability Zone:");

        // Zone combobox
        zoneCbx = new JComboBox(AWSUtils.ZONES);
        zoneCbx.setSelectedIndex(getIdForZone(props.getProperty(AWSUtils.ZONE)));
        zoneCbx.setPreferredSize(new Dimension(150, zoneCbx.getPreferredSize().height));
        zoneCbx.setMaximumSize(new Dimension(150, zoneCbx.getPreferredSize().height));

        // Instance number label
        instanceNumberLbl = new JLabel("Number of instances:");

        // Instance number textfield
        instanceNumberTtf = new JTextField(5);
        instanceNumberTtf.setEditable(true);
        instanceNumberTtf.setPreferredSize(new Dimension(100, instanceNumberTtf.getPreferredSize().height));
        instanceNumberTtf.setMaximumSize(new Dimension(100, instanceNumberTtf.getPreferredSize().height));

        // Default value.
        instanceNumberTtf.setText("1");


        // Launch button for startin the instances
        launchInstanceBtn = new JButton("Launch");
        launchInstanceBtn.setToolTipText("Launch the instance(s)");
        launchInstanceBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                launchPressed();
            }
        });

        instancePnl.setLayout(new BoxLayout(instancePnl, BoxLayout.X_AXIS));
        instancePnl.add(Box.createHorizontalStrut(5));
        instancePnl.add(imageIdLbl);
        instancePnl.add(Box.createHorizontalStrut(5));
        instancePnl.add(imageIdTtf);
        instancePnl.add(Box.createHorizontalStrut(20));
        instancePnl.add(zoneLbl);
        instancePnl.add(Box.createHorizontalStrut(5));
        instancePnl.add(zoneCbx);
        instancePnl.add(Box.createHorizontalStrut(20));
        instancePnl.add(instanceNumberLbl);
        instancePnl.add(Box.createHorizontalStrut(5));
        instancePnl.add(instanceNumberTtf);
        instancePnl.add(Box.createHorizontalStrut(5));
        instancePnl.add(launchInstanceBtn);
        instancePnl.add(Box.createHorizontalStrut(5));
        instancePnl.add(Box.createHorizontalGlue());
    }

    /**
     * This method is called by the CredentialsDialog to initialize the AWS Credentials parameters.
     * @param keypair The AWS key pair
     * @param group The AWS group
     * @param accesskey The AWS access key
     * @param secretkey The AWS secret key
     */
    public void setCredentials(String keypair, String group, String accesskey, String secretkey) {
        if (accesskey.equals("")) {
            accesskey = null;
            secretkey = null;
        }
        credentials = new Credentials(keypair, group, accesskey, secretkey);
        AWSUtils.setCredentials(credentials);
        props = AWSUtils.getProperties();
    }

    /**
     * This method is called by the ProjectSettingsDialog to initialize the Project settings parameters.
     * @param dbUrl Database URL.
     * @param dbUser Database user.
     * @param dbPass Database password.
     * @param dbName Database table name.
     * @param projectTitle Project title.
     * @param fastaDB FASTA database.
     * @param precursorUnit The precursor unit.
     * @param precursorTol The precursor tolerance.
     * @param fragmentIonTol The fragment ion tolerance.
     */
    public void setProjectSettings(String dbUrl, String dbUser, String dbPass, String dbName, String projectTitle, String fastaDB, String precursorUnit, String precursorTol, String fragmentIonTol) {
        if (dbUser.equals("")) {
            dbUser = null;
            dbPass = null;
        }
        props = AWSUtils.getProperties();
        settings = new ProjectSettings(dbUrl, dbUser, dbPass, dbName, projectTitle, fastaDB, props.getProperty(AWSUtils.BUCKET), precursorUnit, precursorTol, fragmentIonTol);
        AWSUtils.setProjectSettings(settings);


        statusPnl.getProjectFieldLabel().setText(projectTitle);
        statusPnl.getTaxonFieldLabel().setText(fastaDB);
    }

    /**
     * Starts the job(s) for the selected filename(s).
     */
    private void startJob(){
        // Start the job worker.
        final JobWorker jobWorker = new JobWorker();
        jobWorker.start();
    }

    /**
     * Delete the message for the selected messageId.
     */
    private void deleteMessage(){
        // Start the delete message worker.
        final DeleteMessageWorker deleteWorker = new DeleteMessageWorker(getSelectedMessageId());
        deleteWorker.start();
    }

    private void selectStoredFilesInTable() {
        // Clear previously selected rows.
        storageTbl.getSelectionModel().clearSelection();
        boolean flag = false;

        for(int i = 0; i < model.getRowCount(); i++) {
            for(File file : chunkedFiles) {
                if(model.getValueAt(i, 0).equals(file.getName())) {
                    if(!flag) {
                        storageTbl.setRowSelectionInterval(i, i);
                        flag = true;
                    } else {
                        storageTbl.addRowSelectionInterval(i, i);
                    }
                }
            }
        }
    }

    /**
     * Refreshes the table by collecting all StorageObjects.
     */
    public void refreshStorageTable() {
        // First clear the table rows
        for( int i = model.getRowCount() - 1; i >= 0; i-- ) {
            model.removeRow(i);
        }

        // Update the table with all the stored values.
        StorageObject[] storageObjects = cloudTools.listStorageObjects();
        if(storageObjects != null) {
            for (StorageObject obj : storageObjects) {
                updateTable(obj);
            }
        }
        bucketTtf.setText(props.getProperty(AWSUtils.BUCKET));
    }

    /**
     * Refreshes the dataqueue table by collecting all MessageObjects.
     */
    private void refreshDataQueueTable() {
        // First clear the table rows
        for( int i = dataQueueTableMdl.getRowCount() - 1; i >= 0; i-- ) {
            dataQueueTableMdl.removeRow(i);
        }

        // Update the table with all the message values.
        final MessageWorker messageWorker = new MessageWorker();
        messageWorker.start();
    }

    /**
     * Refreshes the instance table by collecting all InstanceObjects.
     */
    private void refreshInstancesTable() {
        // First clear the table rows
        for( int i = instancesTableMdl.getRowCount() - 1; i >= 0; i-- ) {
            instancesTableMdl.removeRow(i);
        }

        // Update the table with all the instance values.
        final InstanceWorker instanceWorker = new InstanceWorker();
        instanceWorker.start();
    }

    /**
     * Refreshes the dataqueue table by collecting all MessageObjects.
     */
    private void refreshStatusTable() {
        clearStatusTable();


        // Update the table with all the status values.
        final StatusWorker statusWorker = new StatusWorker();
        statusWorker.start();

    }

    private void clearStatusTable() {
        // Clear the table rows
        for( int i = jobStatusTableMdl.getRowCount() - 1; i >= 0; i-- ) {
            jobStatusTableMdl.removeRow(i);
        }
    }

    /**
     * This method constructs the data queue table.
     */
    private void constructDataQueueTable() {
        String[] columns = { "Message Id", "Filename", "Receipt Handle"};
        Object[][] data = new Object[0][0];
        dataQueueTableMdl = new DefaultTableModel(data, columns);
        dataQueueTbl = new JTable(dataQueueTableMdl);
        dataQueueTbl.setCellSelectionEnabled(true);
        dataQueueTbl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        TableColumn column;
        for (int i = 0; i < 3; i++) {
            column = dataQueueTbl.getColumnModel().getColumn(i);
            if (i == 0) {
                column.setPreferredWidth(130);
            } else if (i == 1) {
                column.setPreferredWidth(200);
            }
        }
    }

    /**
     * Constructs the instances table.
     */
    private void constructInstancesTable() {
        String[] columns = { "Instance ID", "State", "Image", "Public DNS"};
        Object[][] data = new Object[0][0];
        instancesTableMdl = new DefaultTableModel(data, columns);
        instancesTbl = new JTable(instancesTableMdl);
        instancesTbl.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                int row = instancesTbl.getSelectedRow();
                if(row != -1) {
                    instanceTtf.setText(instancesTbl.getValueAt(row, 0).toString());
                    stopInstanceBtn.setEnabled(true);
                }
            }
        });
        instancesTbl.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
        TableColumn column;
        for (int i = 0; i < 4; i++) {
            column = instancesTbl.getColumnModel().getColumn(i);
            if (i == 0) {
                column.setPreferredWidth(80);
            } else if (i == 1) {
                column.setPreferredWidth(80);
            } else if (i == 2) {
                column.setPreferredWidth(80);
            } else if (i == 3) {
                column.setPreferredWidth(160);
            }
        }
    }

    /**
     * Constructs the instances table.
     */
    private void constructJobStatusTable() {
        String[] columns = { "Instance ID", "Current Job", "Current File", "Error"};
        Object[][] data = new Object[0][0];
        jobStatusTableMdl = new DefaultTableModel(data, columns);
        jobStatusTbl = new JTable(jobStatusTableMdl);

        //jobStatusTbl.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        TableColumn column;
        for (int i = 0; i < 4; i++) {
            column = jobStatusTbl.getColumnModel().getColumn(i);
            if (i == 0) {
                column.setPreferredWidth(50);
            } else if (i == 1) {
                column.setPreferredWidth(60);
            } else if (i == 2) {
                column.setPreferredWidth(160);
            } else if (i == 3) {
                column.setPreferredWidth(130);
            }
        }
        job2RowMap = new HashMap<String, Integer>();
    }

    /**
     * Construct the menu.
     */
    private void constructMenu() {

        // Instantiate MenuBar
        JMenuBar menuBar = new JMenuBar();

        // Defining the menus
        JMenu fileMenu = new JMenu("File");
        fileMenu.setMnemonic('F');

        JMenu optionsMenu = new JMenu("Options");
        optionsMenu.setMnemonic('O');

        JMenu helpMenu = new JMenu("Help");
        helpMenu.setMnemonic('H');
        menuBar.add(fileMenu);
        menuBar.add(optionsMenu);
        menuBar.add(helpMenu);

        // About menu item
        JMenuItem aboutMenuItem = new JMenuItem();
        aboutMenuItem.setMnemonic('a');
        aboutMenuItem.setText("About");
        aboutMenuItem.addActionListener(new java.awt.event.ActionListener() {

            public void actionPerformed(ActionEvent e) {
                aboutTriggered();
            }
        });
        helpMenu.add(aboutMenuItem);

        // New project menu item
        JMenuItem newProjectMenuItem = new JMenuItem();
        newProjectMenuItem.setAccelerator(KeyStroke.getKeyStroke(KeyEvent.VK_N, InputEvent.CTRL_MASK));
        newProjectMenuItem.setMnemonic('N');
        newProjectMenuItem.setText("New Project");
        newProjectMenuItem.setToolTipText("New Project");
        newProjectMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                ProjectSettingsDialog.createProjectSettingsDialog(proteoCloudUI, true, 1);
            }
        });
        fileMenu.add(newProjectMenuItem);

        // Exit menu
        JMenuItem exitMenuItem = new JMenuItem();
        exitMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_X, InputEvent.CTRL_MASK));
        exitMenuItem.setMnemonic('x');
        exitMenuItem.setText("Exit");
        exitMenuItem.setToolTipText("Exit ProteoCloud");
        exitMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                close();
            }
        });
        fileMenu.addSeparator();
        fileMenu.add(exitMenuItem);

        // Project settings menu item
        JMenuItem projectSettingsMenuItem = new JMenuItem();
        projectSettingsMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_S, java.awt.event.InputEvent.ALT_MASK));
        projectSettingsMenuItem.setMnemonic('s');
        projectSettingsMenuItem.setText("Project Settings");
        projectSettingsMenuItem.setToolTipText("The Project Settings");
        projectSettingsMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                ProjectSettingsDialog.createProjectSettingsDialog(proteoCloudUI, true);
            }
        });
        optionsMenu.add(projectSettingsMenuItem);

        // Credentials menu item
        JMenuItem credentialsMenuItem = new JMenuItem();
        credentialsMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_C, java.awt.event.InputEvent.ALT_MASK));
        credentialsMenuItem.setMnemonic('c');
        credentialsMenuItem.setText("AWS Credentials");
        credentialsMenuItem.setToolTipText("The AWS Credentials");
        credentialsMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                ProjectSettingsDialog.createProjectSettingsDialog(proteoCloudUI, true, 2);
            }
        });
        optionsMenu.add(credentialsMenuItem);

        // Database settings menu item
        JMenuItem dbSettingsMenuItem = new JMenuItem();
        dbSettingsMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_D, java.awt.event.InputEvent.ALT_MASK));
        dbSettingsMenuItem.setMnemonic('d');
        dbSettingsMenuItem.setText("Database Settings");
        dbSettingsMenuItem.setToolTipText("The Database Settings");
        dbSettingsMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                ProjectSettingsDialog.createProjectSettingsDialog(proteoCloudUI, true, 3);
            }
        });
        optionsMenu.add(dbSettingsMenuItem);

        // Bucket settings menu item
        JMenuItem bucketSettingsMenuItem = new JMenuItem();
        bucketSettingsMenuItem.setAccelerator(javax.swing.KeyStroke.getKeyStroke(java.awt.event.KeyEvent.VK_B, java.awt.event.InputEvent.ALT_MASK));
        bucketSettingsMenuItem.setMnemonic('b');
        bucketSettingsMenuItem.setText("Bucket Selection");
        bucketSettingsMenuItem.setToolTipText("The S3 Bucket Selection");
        bucketSettingsMenuItem.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent evt) {
                ProjectSettingsDialog.createProjectSettingsDialog(proteoCloudUI, true, 4);
            }
        });
        optionsMenu.add(bucketSettingsMenuItem);
        setJMenuBar(menuBar);
    }

    /**
     * Construct the panel including the table with the storrage objects.
     */
    private void contructStoragePanel() {

        storagePnl = new JPanel(new FormLayout("5dlu, f:p:g, 5dlu", "5dlu, t:p, 5dlu, t:p, 5dlu, p, 5dlu"));
        String[] columns = { "Filename", "Size", "Last modified" };
        Object[][] data = new Object[0][0];
        model = new DefaultTableModel(data, columns);
        storageTbl = new JTable(model);
        storageTbl.setCellSelectionEnabled(true);
        storageTbl.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
        storageTbl.setPreferredSize(new Dimension(500, 300));

        TableColumn column;
        for (int i = 0; i < 3; i++) {
            column = storageTbl.getColumnModel().getColumn(i);
            if (i == 0) {
                column.setPreferredWidth(360);
            } else if (i == 1) {
                column.setPreferredWidth(40);
            } else if (i == 2) {
                column.setPreferredWidth(100);
            }
        }

        JScrollPane scrollPane = new JScrollPane(storageTbl);
        storagePnl.add(inputPnl, cc.xy(2, 2));

        JPanel middlePnl = new JPanel(new FormLayout("f:p:g, 5dlu, p, 5dlu", "t:p"));
        middlePnl.setBorder(BorderFactory.createTitledBorder("S3 Bucket"));
        // Storage file panel
        JPanel storageFilePnl = new JPanel();
        storageFilePnl.setLayout(new FormLayout("5dlu, p:g, 5dlu", "5dlu, f:p:g, 5dlu"));

        storageFilePnl.add(scrollPane, cc.xy(2, 2));
        storageFilePnl.setBorder(BorderFactory.createTitledBorder("Storage Files"));

        JPanel storageControlPnl = new JPanel(new FormLayout("5dlu, p, 5dlu", "5dlu, p, 2dlu, p, 7dlu, p, 2dlu, p, 10dlu, p, 5dlu"));
        storageControlPnl.setBorder(BorderFactory.createTitledBorder("File Control"));

        JLabel bucketLbl = new JLabel("Bucket Name:");
        JLabel selectedFileLbl = new JLabel("No. Selected Files: ");

         // Upload button
        startJobBtn = new JButton("Run Search");
        startJobBtn.setEnabled(false);
        startJobBtn.setToolTipText("Runs a search job with the selected files");

        final JTextField selectedFilesTtf = new JTextField(15);
        selectedFilesTtf.setText("0 file(s) selected");
        storageTbl.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                selectedFilesTtf.setText(getSelectedFilenames().size() + " files selected");
                startJobBtn.setEnabled(true);
            }
        });

        bucketTtf = new JTextField(15);
        bucketTtf.setText("No bucket selected");
        bucketTtf.setEditable(false);
        storageControlPnl.add(bucketLbl, cc.xy(2, 2));
        storageControlPnl.add(bucketTtf, cc.xy(2, 4));
        storageControlPnl.add(selectedFileLbl, cc.xy(2, 6));
        storageControlPnl.add(selectedFilesTtf, cc.xy(2, 8));
        storageControlPnl.add(startJobBtn, cc.xy(2, 10));

        startJobBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                startJob();
            }
        });

        middlePnl.add(storageFilePnl, cc.xy(1, 1));
        middlePnl.add(storageControlPnl, cc.xy(3, 1));
        storagePnl.add(middlePnl, cc.xy(2, 4));
        storagePnl.add(instancePnl, cc.xy(2, 6));
    }

    /**
     * Construct the data queue panel.
     */
    private void contructDataQueuePanel() {
        dataQueuePnl = new JPanel(new FormLayout("5dlu, f:p:g, 5dlu, p, 5dlu", "5dlu, t:p, 5dlu"));
        dataQueuePnl.setBorder(BorderFactory.createTitledBorder("Data Queue"));
        dataQueuePnl.setMaximumSize(new Dimension(dataQueuePnl.getMaximumSize().width, dataQueuePnl.getMaximumSize().height));
        constructDataQueueTable();
        if(cloudTools.areCredentialsProvided()) {
            DataQueueListener.getInstance().addPropertyChangeListener(new PropertyChangeListener() {
                public void propertyChange(PropertyChangeEvent evt) {
                    if (evt.getPropertyName().equals("New Message")){
                        updateTable(evt.getNewValue());
                    }
                }
            });
            refreshDataQueueTable();
        }

        JPanel queuedMessagesPnl = new JPanel(new FormLayout("5dlu, f:p:g, 5dlu", "5dlu, t:p, 5dlu"));
        queuedMessagesPnl.setBorder(BorderFactory.createTitledBorder("Queue Control"));
        JScrollPane scrollPane = new JScrollPane(dataQueueTbl);
        queuedMessagesPnl.add(scrollPane, cc.xy(2, 2));

        JPanel queueControlPnl = new JPanel(new FormLayout("5dlu, p, 5dlu", "5dlu, p, 2dlu, p, 7dlu, p, 10dlu, p, 5dlu"));
        queueControlPnl.setBorder(BorderFactory.createTitledBorder("Queued File Messages"));

        // Refresh Queue button
        refreshQueueBtn = new JButton("Refresh Queue");
        refreshQueueBtn.setEnabled(true);
        refreshQueueBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                refreshDataQueueTable();
            }
        });

        // Delete  button
        deleteBtn = new JButton("Delete Message");
        deleteBtn.setEnabled(false);
        deleteBtn.addActionListener(new ActionListener() {
            public void actionPerformed(ActionEvent e) {
                deleteMessage();
            }
        });

        dataQueueTbl.getSelectionModel().addListSelectionListener(new ListSelectionListener() {
            public void valueChanged(ListSelectionEvent e) {
                int row = dataQueueTbl.getSelectedRow();
                if(row != -1) {
                    messageTtf.setText(dataQueueTbl.getValueAt(row, 1).toString());
                    deleteBtn.setEnabled(true);
                }
            }
        });

        JLabel messageLbl = new JLabel("Selected message:");
        messageTtf = new JTextField(15);
        messageTtf.setText("No message selected");
        messageTtf.setEditable(false);

        queueControlPnl.add(messageLbl, cc.xy(2, 2));
        queueControlPnl.add(messageTtf, cc.xy(2, 4));

        queueControlPnl.add(deleteBtn, cc.xy(2, 6));
        queueControlPnl.add(refreshQueueBtn, cc.xy(2, 8));

        dataQueuePnl.add(queuedMessagesPnl, cc.xy(2, 2));
        dataQueuePnl.add(queueControlPnl, cc.xy(4, 2));
    }

    /**
     * This happens when the upload button is used.
     */
    private void uploadPressed() {
        File[] files = new File[spectraFiles.size()];
        final ChunkWorker chunkWorker = new ChunkWorker(spectraFiles.toArray(files));
        chunkWorker.start();
    }

    /**
     * Delete the files.
     * @param files MGF files.
     */
    private void deleteFiles(List<File> files) {
            for (File file : files) {
                if (file.getName().endsWith("mgf")) {
                    file.delete();
                }
            }
    }

    /**
     * Progress that happens when the launch-button is pressed.
     */
    private void launchPressed() {
        // Create a progressbar, a TextArea and a dialog to house the area and bar.
        final JDialog progressDialog = new JDialog(proteoCloudUI, "Launch Progress", true);
        progressDialog.setLayout(new BorderLayout());
        final JProgressBar progressBar = new JProgressBar();
        progressBar.setStringPainted(true);
        progressBar.setString("");
        JPanel progPanel = new JPanel();
        progPanel.setMinimumSize(new Dimension(500, 80));
        progPanel.setPreferredSize(new Dimension(500, 80));
        progPanel.setLayout(new BoxLayout(progPanel, BoxLayout.X_AXIS));
        progPanel.add(Box.createHorizontalStrut(15));
        progPanel.add(progressBar);
        progPanel.add(Box.createHorizontalStrut(15));
        progressDialog.add(progPanel);
        progressBar.setIndeterminate(true);
        progressBar.setString("Launching instances...");
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(proteoCloudUI);
        final int instancenumber = Integer.valueOf(instanceNumberTtf.getText());
        // Iterate over all the spectra files.

        //progressDialog.setTitle("Uploading " + spectraFiles.get(i).getName());
        if (instancenumber == 1){
            progressBar.setString("Launching one new instance...");
        } else {
            progressBar.setString("Launching " + instancenumber + " new instances...");
        }

        //progressBar.setValue(i);
        final LaunchInstanceWorker instanceWorker = new LaunchInstanceWorker(instancenumber, progressDialog);
        instanceWorker.start();
        progressDialog.setVisible(true);
    }

    /**
     * Progress that happens when the stop-button is pressed.
     */
    private void stopPressed() {
        // Create a progressbar, a TextArea and a dialog to house the area and bar.
        final JDialog progressDialog = new JDialog(proteoCloudUI, "Stop Progress", true);
        progressDialog.setLayout(new BorderLayout());
        final JProgressBar progressBar = new JProgressBar();
        progressBar.setStringPainted(true);
        progressBar.setString("");
        JPanel progPanel = new JPanel();
        progPanel.setMinimumSize(new Dimension(500, 80));
        progPanel.setPreferredSize(new Dimension(500, 80));
        progPanel.setLayout(new BoxLayout(progPanel, BoxLayout.X_AXIS));
        progPanel.add(Box.createHorizontalStrut(15));
        progPanel.add(progressBar);
        progPanel.add(Box.createHorizontalStrut(15));
        progressDialog.add(progPanel);
        progressBar.setIndeterminate(true);
        progressBar.setString("Terminating instance...");
        progressDialog.pack();
        progressDialog.setLocationRelativeTo(proteoCloudUI);
        progressBar.setString("Terminating instance...");
        final StopInstanceWorker stopInstanceWorker = new StopInstanceWorker(progressDialog);
        stopInstanceWorker.start();
        progressDialog.setVisible(true);
    }

    /**
     * This method updates the table according to the StorageObject value retrieved.
     * @param newValue
     */
    private void updateTable(Object newValue){

        // Check if object is an instance of the storage object.
        if(newValue instanceof StorageObject){
            StorageObject storageObj = (StorageObject) newValue;
            storageTbl.getRowCount();
            Object[] data = new Object[3];
            data[0] = storageObj.getFilename();
            data[1] = (storageObj.getSize() / 1024) + " kb";
            data[2] = storageObj.getLastModified();
            model.insertRow(storageTbl.getRowCount(), data);
        }

        // MessageObject
        if(newValue instanceof MessageObject){
            MessageObject messageObj = (MessageObject) newValue;
            dataQueueTbl.getRowCount();
            Object[] data = new Object[3];
            data[0] = messageObj.getMessageid();
            data[1] = messageObj.getMessageBody().substring(0, messageObj.getMessageBody().indexOf(" "));
            data[2] = messageObj.getReceipthandle();
            dataQueueTableMdl.insertRow(dataQueueTbl.getRowCount(), data);
        }

        // InstanceObject
        if(newValue instanceof EC2Instance){
            EC2Instance instanceObj = (EC2Instance) newValue;
            instancesTbl.getRowCount();
            Object[] data = new Object[4];
            data[0] = instanceObj.getInstanceID();
            data[1] = instanceObj.getState();
            data[2] = instanceObj.getImage();
            data[3] = instanceObj.getPublicDNS();
            instancesTableMdl.insertRow(instancesTbl.getRowCount(), data);
        }

        // JobObject
        if(newValue instanceof CloudJob){
            CloudJob jobObj = (CloudJob) newValue;
            int rows = jobStatusTbl.getRowCount();
            // Check if table is empty.

            if (job2RowMap.get(jobObj.getInstanceID()) != null){
                for (int i = 0; i < rows; i++) {
                    String value = jobStatusTableMdl.getValueAt(i, 0).toString();
                    if (value.equals(jobObj.getInstanceID())) {
                        jobStatusTableMdl.setValueAt(jobObj.getCurrentJob(), i,	1);
                        jobStatusTableMdl.setValueAt(jobObj.getCurrentFile(), i,	2);
                        jobStatusTableMdl.setValueAt(jobObj.getError(), i,	3);
                    }
                }
                jobStatusTableMdl.fireTableDataChanged();
            } else {
                Object[] data = new Object[4];
                data[0] = jobObj.getInstanceID();
                data[1] = jobObj.getCurrentJob();
                data[2] = jobObj.getCurrentFile();
                data[3] = jobObj.getError();

                job2RowMap.put(jobObj.getInstanceID(), jobStatusTbl.getRowCount());
                jobStatusTableMdl.insertRow(jobStatusTbl.getRowCount(), data);
            }
        }
    }

    /**
     * Retrieves the filename(s) from the selected cell(s) and returns the filenames list.
     * @return
     */
    private List<String> getSelectedFilenames() {
        List<String> filenames = new ArrayList<String>();

        int rowIndexStart = storageTbl.getSelectedRow();
        int rowIndexEnd = storageTbl.getSelectionModel().getMaxSelectionIndex();
        int colIndexStart = storageTbl.getSelectedColumn();
        int colIndexEnd = storageTbl.getColumnModel().getSelectionModel() .getMaxSelectionIndex();
        // Check each cell in the range
        for(int row=rowIndexStart; row<=rowIndexEnd; row++) {
            for (int col=colIndexStart; col<=colIndexEnd; col++) {
                if (storageTbl.isCellSelected(row, col)) { // cell is selected
                    filenames.add(storageTbl.getModel().getValueAt(row, col).toString());
                }
            }
        }
        return filenames;
    }

    /**
     * Retrieves the messages(s) from the selected cell(s) and returns the filenames list.
     * @return
     */
    private String getSelectedMessageId() {
        String messageId = "";
        int row = dataQueueTbl.getSelectedRow();
        if (row != -1) {
            messageId = dataQueueTbl.getValueAt(row, 0).toString();
        }

        return messageId;

    }

    /**
     * Retrieves the instances(s) from the selected cell(s) and returns an string array.
     * @return
     */
    private String getSelectedInstance() {
        int row = instancesTbl.getSelectedRow();
        String selectedInstance = "";
        if (row != -1){
            selectedInstance = instancesTbl.getModel().getValueAt(row, 0).toString();
        }
        return selectedInstance;
    }

    public void getResults(String projectTitle) {
        ResultsWorker worker = new ResultsWorker(projectTitle);
        worker.start();
    }

    /**
     * ResultsWorker extends the default SwingWorker and handles the retrieval of the results in a separate thread.
     */
    private class ResultsWorker extends SwingWorker {

        private String projectTitle;
        private ProgressDialog progressDialog;
        final CloudTools cloudTools = CloudTools.getInstance();

        /**
         * Constructor for the ResultsWorker.
         *
         * @param projectTitle The project title
         */
        public ResultsWorker(String projectTitle) {
            this.projectTitle = projectTitle;
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    if(!dbSearchResult.isEmpty() || !denovoSearchResult.isEmpty()) {
                        cloudTools.firePropertyChange("new message", null, "Populating Results Tables");
                        cloudTools.firePropertyChange("indeterminate", false, true);
                        dbResultPnl.updateResults(dbSearchResult);
                        denovoResultPnl.updateResults(denovoSearchResult);
                        cloudTools.firePropertyChange("close", null, "Populated Tables Finished");
                        tabbedPane.setEnabledAt(4, true);
                        tabbedPane.setSelectedIndex(4);
                    } else {
                        cloudTools.firePropertyChange("close", null, "No Results Found");
                    }

                }
            });
        }

        /**
         * Main task for retrieving the results, executed in background thread.
         */
        public Object construct() {
            final CloudTools cloudTools = CloudTools.getInstance();
            // Search result retrieval
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    progressDialog = new ProgressDialog(cloudTools, proteoCloudUI, "Retrieving Database Search Results", true);
                }
            });

            try {
                 dbSearchResult = cloudTools.getDbSearchResult(projectTitle);
                 denovoSearchResult = cloudTools.getDenovoSearchResult(projectTitle);
            } catch (ConnectException e) {
                JOptionPane.showMessageDialog(proteoCloudUI, e.getMessage(), "Please provide valid database settings.", JOptionPane.ERROR_MESSAGE);
            }

            finished();
            return 0;
        }
    }

    /**
     * ChunkWorker extends the default SwingWorker and manages to chunk the files
     * in a separate thread.
     */
    private class ChunkWorker extends SwingWorker {
        private File[] files;
        private ProgressDialog progressDialog;
        /**
         * Constructor for the ChunkWorker, chunks one file per task.
         *
         * @param files
         */
        public ChunkWorker(File[] files) {
            this.files = files;
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
            // Start the upload worker after the files have been chunked.
            final UploadWorker uploadWorker = new UploadWorker();
            uploadWorker.start();
        }

        /**
         * Main task for chunking the files, executed in background thread.
         */
        public Object construct() {
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    progressDialog = new ProgressDialog(cloudTools, proteoCloudUI, "Chunk Progress", true);
                }
            });
            // Actually chunk the files.
            chunkedFiles = CloudTools.getInstance().chunkFiles(files, 1000);
            return null;
        }
    }

    /**
     * JobWorker extends the default SwingWorker for starting com.compomics.proteocloud.jobs in a separate thread.
     */
    private class JobWorker extends SwingWorker {

        /**
         * Constructor for the MessageWorker.
         *
         */
        public JobWorker() {
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
        }

        /**
         * Main task for uploading the files, executed in background thread.
         */
        public Object construct() {
            Properties props = AWSUtils.getProperties();

            try {
                msgManager = new MessageManager(props, "dataQueue");
            } catch (SQSException e) {
                JOptionPane.showMessageDialog(null, e.getLocalizedMessage() + "\n" + "Please make sure that you provided the credentials in the proteocloud_empty.properties file can be found.",
                        "", JOptionPane.ERROR_MESSAGE);
            }
            List<String> filenames = getSelectedFilenames();

            String bucket = props.getProperty(AWSUtils.BUCKET);
            for (String file : filenames) {
                try {
                    msgManager.sendMessage(file, settings.getProjectTitle(), settings.getDbName(), settings.getDbUrl(), settings.getDbUser(), settings.getDbPass(), settings.getFastaDatabase(), settings.getFragmentIonTol(), settings.getPrecursorTol(), settings.getPrecursorUnit(), settings.getBucket());
                } catch (SQSException e1) {
                    e1.printStackTrace();
                }
            }
            return null;
        }
    }

    /**
     * MessageWorker extends the default SwingWorker and manages receiving the messages
     * in a separate thread.
     */
    private class MessageWorker extends SwingWorker {

        /**
         * Constructor for the MessageWorker.
         *
         */
        public MessageWorker() {
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
        }

        /**
         * Starting to listen to the data queue.
         */
        public Object construct() {
            DataQueueListener.getInstance().start();
            return null;
        }
    }

    /**
     * DeleteMessageWorker extends the default SwingWorker and deletes the selected message in a separate thread.
     */
    private class DeleteMessageWorker extends SwingWorker {
        private String messageId;

        /**
         * Constructor for the DeleteMessageWorker.
         *
         */
        public DeleteMessageWorker(String messageId) {
            this.messageId = messageId;
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
            // Reset message textfield.
            messageTtf.setText("No message selected");

            // Refresh the data queue table
            refreshDataQueueTable();
            // Show dialog that upload has been completed.
            JOptionPane.showMessageDialog(proteoCloudUI, "The file message " + messageId + " has been deleted from the queue.");
        }

        /**
         * Starting to listen to the data queue.
         */
        public Object construct() {
            try {
                msgManager.deleteMessageByMessageId(messageId);
            } catch (SQSException e1) {
                e1.printStackTrace();
            }
            return null;
        }
    }

    /**
     * StatusWorker extends the default SwingWorker and manages receiving the messages
     * in a separate thread.
     */
    private class StatusWorker extends SwingWorker {

        /**
         * Constructor for the StatusWorker.
         *
         */
        public StatusWorker() {
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
        }

        /**
         * Starting to listen to the status queue.
         */
        public Object construct() {
            StatusQueueListener.getInstance().start();
            return null;
        }
    }

    /**
     * UploadWorker extends the default SwingWorker and manages to upload the
     * data to the S3 storage in a seperate thread.
     */
    private class UploadWorker extends SwingWorker {

        private ProgressDialog progressDialog;

        /**
         * Constructor for the UploadWorker, uploads one file per task.
         */
        public UploadWorker() {
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
             // Refresh the table for the new data.
            refreshStorageTable();

            selectStoredFilesInTable();
            
            deleteFiles(chunkedFiles);
             // Empty array list
            chunkedFiles.clear();

            // Empty the array list
            spectraFiles.clear();

            // Show dialog that upload has been completed.
            JOptionPane.showMessageDialog(proteoCloudUI, "All spectra files have been uploaded successfully.");
            spectraFilesTtf.setText("0 file(s) selected");
            uploadSpectraBtn.setEnabled(false);
        }

        /**
         * Main task for uploading the files, executed in background thread.
         */
        public Object construct() {
            // Actually upload the file.
            EventQueue.invokeLater(new Runnable() {
                public void run() {
                    new ProgressDialog(cloudTools, proteoCloudUI, "Uploading Files", true);
                }
            });
            CloudTools.getInstance().uploadFile(chunkedFiles);

            return null;
        }
    }

    /**
     * LaunchInstanceWorker extends the default SwingWorker and handles the EC2 instance launching.
     */
    private class LaunchInstanceWorker extends SwingWorker {

        private int numberOfInstances;
        private JDialog progressDialog;

        /**
         * Constructor for the InstanceWorker, uploads one file per task.
         *
         * @param numberOfInstances The number of instances.
         * @param progressDialog
         */
        public LaunchInstanceWorker(int numberOfInstances, JDialog progressDialog) {
            this.numberOfInstances = numberOfInstances;
            this.progressDialog = progressDialog;
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
            refreshInstancesTable();
            progressDialog.setVisible(false);
        }

        /**
         * Main task for uploading the files, executed in background thread.
         */
        public Object construct() {
            CloudTools tools = CloudTools.getInstance();
            tools.launchInstances(numberOfInstances);
            return null;
        }
    }

    /**
     * StopInstanceWorker extends the default SwingWorker and handles the EC2 instance stopping.
     */
    private class StopInstanceWorker extends SwingWorker {

        private JDialog progressDialog;

        /**
         * Constructor for the StopInstanceWorker.
         *
         * @param progressDialog
         */
        public StopInstanceWorker(JDialog progressDialog) {
            this.progressDialog = progressDialog;
        }

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
            refreshInstancesTable();
            progressDialog.setVisible(false);
        }

        /**
         * Main task for uploading the files, executed in background thread.
         */
        public Object construct() {
            String[] instanceIds = new String[1];
            instanceIds[0] = getSelectedInstance();
            CloudTools tools = CloudTools.getInstance();
            tools.terminateInstances(instanceIds);
            return null;
        }
    }

    /**
     * InstanceWorker extends the default SwingWorker and handles the EC2 instance management.
     */
    private class InstanceWorker extends SwingWorker {

        /**
         * This variable holds the number of running instances.
         */
        private int runningCount = 0;

        /**
         * The currently available EC2 instances.
         */
        private EC2Instance[] instanceObjects = null;
        /**
         * Constructor for the InstanceWorker.
         */
        public InstanceWorker() {}

        /**
         * Executed in event dispatching thread.
         */
        public void finished() {
            if(instanceObjects != null){

                for (EC2Instance obj : instanceObjects) {
                    if(obj.getState().equalsIgnoreCase("running")) runningCount++;
                    updateTable(obj);
                }
                // Refreshing is possible if instance objects exist.
                if(instanceObjects.length > 0) refreshInstancesBtn.setEnabled(true);
            }
            statusPnl.getInstancesFieldLabel().setText(Integer.toString(runningCount));
        }

        /**
         * Main task for uploading the files, executed in background thread.
         */
        public Object construct() {
            // Get the instance objects.
            instanceObjects = CloudTools.getInstance().getInstanceObjects();
            return null;
        }
    }

    /**
     * Helper method for the zones.
     * @param zone
     * @return
     */
    private int getIdForZone(String zone){
        int id = -1;
        if (zone.equals("us-east-1a")) id = 0;
        else if (zone.equals("us-east-1b")) id = 1;
        else if (zone.equals("us-east-1c")) id = 2;
        else if (zone.equals("us-east-1d")) id = 3;
        return id;
    }

    /**
     *  Returns an ImageIcon, or null if the path was invalid.
     */
    private static ImageIcon createImageIcon(String path) {
        URL imgURL = ProteoCloudUI.class.getResource(path);
        if (imgURL != null) {
            return new ImageIcon(imgURL);
        } else {
            System.err.println("Couldn't find file: " + path);
            return null;
        }
    }

    /**
     * The method that builds the about dialog.
     */
    private void aboutTriggered() {
        StringBuffer tMsg = new StringBuffer();
        tMsg.append(PropertiesUI.APPTITLE + " " + PropertiesUI.VERSION);
        tMsg.append("\n");
        tMsg.append("\n");
        tMsg.append("ProteoCloud is a cloud based proteomics pipeline.");
        tMsg.append("\n");
        tMsg.append("\n");
        tMsg.append("The latest version is available at http://proteocloud.googlecode.com");
        tMsg.append("\n");
        tMsg.append("\n");
        tMsg.append("If any questions arise, contact the corresponding author: ");
        tMsg.append("\n");
        tMsg.append("Thilo.Muth@gmx.de");
        tMsg.append("\n");
        tMsg.append("\n");
        tMsg.append("");
        tMsg.append("");
        JOptionPane.showMessageDialog(this, tMsg,
                "About " + PropertiesUI.APPTITLE + " " + PropertiesUI.VERSION, JOptionPane.INFORMATION_MESSAGE);
    }

    /**
     * This method is called when the proteoCloudUI is closed. It shuts down the JVM.
     */
    public void close() {
        System.exit(0);
    }

    /**
     * Main method ==> Entry point to the application.
     *
     * @param args
     */
    public static void main(String[] args) {
        JFrame.setDefaultLookAndFeelDecorated(true);

        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                try {
                    SubstanceLookAndFeel.setSkin(new BusinessBlackSteelSkin());
                } catch (Exception e) {
                    e.printStackTrace();
                }
                new ProteoCloudUI();
            }
        });
    }

    /**
     * Returns an instance of the user interface.
     * @return
     */
    public static ProteoCloudUI getInstance(){
        if(proteoCloudUI == null) {
            proteoCloudUI = new ProteoCloudUI();
        }
        return proteoCloudUI;
    }
}
